import type { DynamicRouteTreeNode, RouterConfig } from '../types';
import Cookies from 'js-cookie';

// @ts-ignore
const Empty = () => import('../Empty.vue');
// @ts-ignore
const NotFound = () => import('../NotFound.vue');

const cachedKey = 'DYNAMIC_ROUTE_CACHE'; // 动态路由缓存键
let currentRouter: any = null; // router 实例
let routeReady: boolean; // 是否已加载动态路由
let shouldResetDynamicRoutesOnLogin = true; // 登录跳转时是否重置动态路由

// 初始化路由
export function initRouter(router: any, options: RouterConfig): void {
  const login = options.loginPagePath || '/login';
  currentRouter = router;
  // 路由守卫
  router.beforeEach(async (to: any, from: any) => {
    // 无需授权，需保证login无需授权
    if (to.meta.noAuth) {
      return true;
    }
    // 未授权跳转登录
    if (!Cookies.get('token')) {
      return login;
    }
    // 是否开启动态路由
    if (!options.modules || !options.dynamicRoutesRequest) {
      return true;
    }
    // 登录页跳转时可选择清除动态路由
    if (from.path === login && shouldResetDynamicRoutesOnLogin) {
      clearDynamicRoutes(router);
    }
    // 是否加载动态路由
    if (!routeReady) {
      shouldResetDynamicRoutesOnLogin = false;
      await initRoutes(router, options);
      return { ...to, replace: true };
    }
    shouldResetDynamicRoutesOnLogin = true;
    return true;
  });
}

// 添加动态路由
export async function initRoutes(router: any, options: RouterConfig): Promise<void> {
  currentRouter = router;
  let menuTree: DynamicRouteTreeNode[] = [];
  // 获取动态路由数据
  const cached = sessionStorage.getItem(cachedKey);
  if (cached) {
    // 先使用缓存解析为对象供快速挂载
    menuTree = JSON.parse(cached || '[]');
    // 异步拉取最新数据并与原始缓存字符串比较
    options.dynamicRoutesRequest!().then((json) => {
      const jsonStr = JSON.stringify(json);
      if (jsonStr === cached) return;
      sessionStorage.setItem(cachedKey, jsonStr);
      router.go(0); // 刷新页面以应用最新路由
    });
  } else {
    menuTree = await options.dynamicRoutesRequest!();
    sessionStorage.setItem(cachedKey, JSON.stringify(menuTree));
  }
  // 将文件路径作为键
  const modules = options.modules || {};
  // 添加动态页(树结构）
  const addRouteTree = (tree: DynamicRouteTreeNode[], deep: number) => {
    tree.forEach((route: DynamicRouteTreeNode) => {
      // 路由参数格式化
      options.routeTreeNodeHandler && options.routeTreeNodeHandler(route);
      // 顶级路由挂载父级
      if (deep == 0 && options.parentRouteName) route.parent = options.parentRouteName;
      // 处理父节点为页面容器的情况
      if (route.children && route.children.length > 0) {
        if (!route.component || route.path === '' || route.path === '/') {
          options.routeTreeNodeHandler && options.routeTreeNodeHandler(route.children[0]);
          route.redirect = { name: route.children[0].name };
        } else {
          route.children.unshift(Object.assign({}, route, { path: '', children: [] }));
          route.component = undefined;
          route.name = `${route.name}Index`;
          route.children.forEach((t: DynamicRouteTreeNode) => {
            t.parent = route.name;
          });
        }
      }
      const routeItem = {
        path: route.path,
        name: route.name,
        redirect: route.redirect,
        meta: Object.assign({}, route.meta || {}, { __isDynamic: true }),
        component: route.component ? modules[route.component] : Empty,
      };
      // 路由路径path以/开头自动会变成顶级路由
      if (route.parent) {
        router.addRoute(route.parent, routeItem);
      } else {
        router.addRoute(routeItem);
      }
      // 递归处理子节点
      if (route.children && route.children.length > 0) {
        route.children.forEach((t: DynamicRouteTreeNode) => {
          t.parent = route.name;
        });
        addRouteTree(route.children, deep + 1);
      }
    });
  };
  addRouteTree(menuTree, 0);
  // 404 页面
  router.addRoute({
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    meta: { noAuth: true, __isDynamic: true },
    component: NotFound,
  });
  routeReady = true;
}

// 移除所有动态路由
export function clearDynamicRoutes(router?: any): string[] {
  const targetRouter = router || currentRouter;
  const removed: string[] = [];
  if (!targetRouter) return removed;
  try {
    const routes = targetRouter.getRoutes ? targetRouter.getRoutes() : [];
    routes.forEach((r: any) => {
      if (r && r.meta && r.meta.__isDynamic && r.name) {
        try {
          targetRouter.removeRoute(r.name);
          removed.push(r.name);
        } catch (e) {
          // ignore individual remove failures
        }
      }
    });
  } finally {
    sessionStorage.removeItem(cachedKey);
    routeReady = false;
  }
  return removed;
}
